// 使用AOP实现装饰者模式
// 首先给出 Function.prototype.before 方法和 Function.prototype.after 方法

Function.prototype.before = function(beforefn) {
    var __self = this; // 保存原函数的引用
    return function() { // 返回包含了原函数和新函数的"代理"函数
        beforefn.apply(this, arguments); // 执行新函数，且保证 this 不被劫持，新函数接受的参数 // 也会被原封不动地传入原函数，新函数在原函数之前执行
        return __self.apply(this, arguments); // 执行原函数并返回原函数的执行结果，  // 并且保证 this 不被劫持
    }
}
Function.prototype.after = function(afterfn) {
    var __self = this;
    return function() {
        var ret = __self.apply(this, arguments);
        afterfn.apply(this, arguments);
        return ret;
    }
};

// AOP 的应用实例
// 数据统计上报 比如页面中有一个登录 button，点击这个 button 会弹出登录浮层，与此同时要进行数据上报， 来统计有多少用户点击了这个登录 button
// 未使用AOP

var showLogin = function() {
    console.log('打开登录浮层');
    log(this.getAttribute('tag'));
}
var log = function(tag) {
    console.log('上报标签为: ' + tag);
    (new Image).src = 'http:// xxx.com/report?tag=' + tag;
}
document.getElementById('button').onclick = showLogin;

// 使用AOP

var showLogin = function() {
    console.log('打开登录浮层');
}
var log = function() {
    console.log('上报标签为: ' + this.getAttribute('tag'));
}
showLogin = showLogin.after(log); // 打开登录浮层之后上报数据
document.getElementById('button').onclick = showLogin;


// 插件式的表单验证 我们很多人都写过许多表单验证的代码，在一个 Web 项目中，可能存在非常多的表单，如 注册、登录、修改用户信息等。在表单数据提交给后台之前，常常要做一些校验，比如登录的时 候需要验证用户名和密码是否为空

// 未使用AOP

var formSubmit = function() {
    if (username.value === '') {
        return alert('用户名不能为空');
    }
    if (password.value === '') {
        return alert('密码不能为空');
    }
    var param = {
        username: username.value,
        password: password.value
    }
    ajax('http:// xxx.com/login', param);
}
submitBtn.onclick = function() {
    formSubmit();
}

// 使用AOP

var validata = function() {
    if (username.value === '') {
        alert('用户名不能为空');
        return false;
    }
    if (password.value === '') {
        alert('密码不能为空');
        return false;
    }
}
var formSubmit = function() {
    var param = {
        username: username.value,
        password: password.value
    }
    ajax('http:// xxx.com/login', param);
}
formSubmit = formSubmit.before(validata);
submitBtn.onclick = function() {
    formSubmit();
}

// 装饰者模式和代理模式的结构看起来非常相像，这两种模式都描述了怎样为对象提供 一定程度上的间接引用，它们的实现部分都保留了对另外一个对象的引用，并且向那个对象发送 请求。
// 代理模式和装饰者模式最重要的区别在于它们的意图和设计目的。代理模式的目的是，当直接访问本体不方便或者不符合需要时，为这个本体提供一个替代者。
// 本体定义了关键功能，而代理提供或拒绝对它的访问，或者在访问本体之前做一些额外的事情
// 。装饰者模式的作用就是为对 象动态加入行为。